[PB Generic表][Go SDK]批量部分字段查询
1. 接口说明
BatchFieldGet,从数据库表中批量查询多条记录的部分字段。
注意:
- 最多批量操作1024条记录;
- 每条请求记录需要填写完整主键字段,返回字段通过
option.PBOpt.FieldNames指定; FieldNames只能填写 value 字段,不能填写 key 字段;嵌套字段使用点号连接,例如pay.amount;- 单条记录的错误存放在
option.PBOpt.BatchResult中,单条记录的版本号存放在option.PBOpt.BatchVersion中; - 调用时要设置
MultiFlag为 1,允许响应分包,避免响应包超过256KB后不分包导致丢包。
更多说明参见批量操作接口的相关说明。
2. 版本要求
3.55.0以上版本SDK。
3. 准备工作
参见准备工作文档,完成使用该接口前的准备工作,并创建如下PB Generic表。game_players表
syntax = "proto3"; // Specify the version of the protocol buffers language
package tcaplusservice;
import "tcaplusservice.optionv1.proto"; // Use the public definitions of TcaplusDB by importing them.
message game_players { // Define a TcaplusDB table with message
// Specify the primary keys with the option tcaplusservice.tcaplus_primary_key
// The primary key of a TcaplusDB table has a limit of 4 fields
option(tcaplusservice.tcaplus_primary_key) = "player_id, player_name, player_email";
// Specify the primary key indexes with the option tcaplusservice.tcaplus_index
option(tcaplusservice.tcaplus_index) = "index_1(player_id, player_name)";
option(tcaplusservice.tcaplus_index) = "index_2(player_id, player_email)";
// Value Types supported by TcaplusDB
// int32, int64, uint32, uint64, sint32, sint64, bool, fixed64, sfixed64, double, fixed32, sfixed32, float, string, bytes
// Nested Types Message
// primary key fields
int64 player_id = 1;
string player_name = 2;
string player_email = 3;
// Ordinary fields
int32 game_server_id = 4;
repeated string login_timestamp = 5;
repeated string logout_timestamp = 6;
bool is_online = 7;
payment pay = 8;
}
message payment {
int64 pay_id = 1;
uint64 amount = 2;
int64 method = 3;
}
准备工作完成后,将会获得以下信息,这些信息在使用SDK时会被用到:
- 目录服务器地址列表
- 业务ID
- 业务访问密码
- 游戏区ID
- 数据表名
4. 示例代码
示例代码的基本执行过程:
- 定义表配置参数
- 设置日志配置;
- 创建客户端;
- 发送请求并处理响应;
- 销毁客户端。
客户端初始化示例:
package main
import (
"fmt"
"git.woa.com/gcloud_storage_group/tcaplus-go-api"
)
// 定义表配置参数
const (
AppId = uint64(2)
ZoneId = uint32(3)
DirUrl = "tcp://x.x.x.x:xxxx"
Signature = "xxxxxxxxxxxxx"
TableName = "game_players"
)
var client *tcaplus.PBClient
func main() {
// 创建客户端
client = tcaplus.NewPBClient()
// 设置日志配置,logconf.xml文件设置了日志级别
if err := client.SetLogCfg("./logconf.xml"); err != nil {
fmt.Println(err.Error())
return
}
// 构造Map对象存储对应表格组下所有的表
zoneList := []uint32{ZoneId}
zoneTable := make(map[uint32][]string)
zoneTable[ZoneId] = []string{TableName}
// 连接TcaplusDB后端
err := client.Dial(AppId, zoneList, DirUrl, Signature, 30, zoneTable)
if err != nil {
fmt.Printf("init failed %v\n", err.Error())
return
}
// 设置默认使用的zone
client.SetDefaultZoneId(ZoneId)
fmt.Printf("Dial finish\n")
PbBatchFieldGetExample()
// 程序退出时调用Close销毁客户端
client.Close()
}
4.1 同步调用示例(推荐)
同步调用编码最简单,通过多协程并发。 示例目录
package main
import (
"fmt"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/table/tcaplusservice"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/tools"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/logger"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/protocol/option"
"google.golang.org/protobuf/proto"
)
func PbBatchFieldGetExample() {
msg1 := &tcaplusservice.GamePlayers{
PlayerId: 10805514,
PlayerName: "Calvin",
PlayerEmail: "calvin@test.com",
}
msg2 := &tcaplusservice.GamePlayers{
PlayerId: 10805515,
PlayerName: "Calvin",
PlayerEmail: "calvin@test.com",
}
msg3 := &tcaplusservice.GamePlayers{
PlayerId: 108055150000,
PlayerName: "Calvin",
PlayerEmail: "calvin@test.com",
}
var msgs []proto.Message
msgs = append(msgs, msg1)
msgs = append(msgs, msg2)
msgs = append(msgs, msg3)
// 设置允许响应分包,并指定本次只查询 game_server_id 和二级字段 pay.amount
opt := &option.PBOpt{
MultiFlag: 1,
FieldNames: []string{"game_server_id", "pay.amount"},
}
err := client.DoBatchFieldGet(msgs, opt)
if err != nil {
logger.ERR("DoBatchFieldGet error:%s", err)
return
}
for i, rspMsg := range msgs {
fmt.Println(tools.ConvertToJson(rspMsg))
fmt.Println(rspMsg.(*tcaplusservice.GamePlayers))
// 单条记录的错误码
fmt.Println("result", opt.BatchResult[i])
// 记录version
fmt.Println("version", opt.BatchVersion[i])
}
logger.INFO("batch field get success")
fmt.Println("batch field get success")
}
4.2 同步调用示例2(推荐)
该示例同步调用方式比较类似C++接口的调用方式,需要创建请求和解析响应。 示例目录
package main
import (
"fmt"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/table/tcaplusservice"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/example/PB/tools"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/logger"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/protocol/cmd"
"git.woa.com/gcloud_storage_group/tcaplus-go-api/terror"
"time"
)
func PbBatchFieldGetExample() {
// 生成 batch field get 请求
req, err := client.NewRequest(tools.Zone, "game_players", cmd.TcaplusApiPBBatchFieldGetReq)
if err != nil {
logger.ERR("NewRequest error:%s", err)
return
}
// 设置分包
req.SetMultiResponseFlag(1)
fieldNames := []string{"game_server_id", "pay.amount"}
msg1 := &tcaplusservice.GamePlayers{
PlayerId: 10805514,
PlayerName: "Calvin",
PlayerEmail: "calvin@test.com",
}
msg2 := &tcaplusservice.GamePlayers{
PlayerId: 10805515,
PlayerName: "Calvin",
PlayerEmail: "calvin@test.com",
}
for _, msg := range []*tcaplusservice.GamePlayers{msg1, msg2} {
// 向请求中添加记录,对于 generic 表 index 无意义,填 0 即可
record, err := req.AddRecord(0)
if err != nil {
logger.ERR("AddRecord error:%s", err)
return
}
// key字段必填,通过第二个参数指定本次查询的部分value字段
_, err = record.SetPBFieldValues(msg, fieldNames)
if err != nil {
logger.ERR("SetPBFieldValues error:%s", err)
return
}
}
// 发送请求,接收响应
resps, err := client.DoMore(req, 5*time.Second)
if err != nil {
logger.ERR("SendRequest error:%s", err)
return
}
for _, resp := range resps {
// 获取响应结果
errCode := resp.GetResult()
if errCode != terror.GEN_ERR_SUC {
logger.ERR("batch field get error:%s", terror.GetErrMsg(errCode))
return
}
// 如果有返回记录则用以下接口进行获取
for i := 0; i < resp.GetRecordCount(); i++ {
record, err := resp.FetchRecord()
if err != nil {
logger.ERR("FetchRecord failed %s", err.Error())
return
}
newMsg := &tcaplusservice.GamePlayers{}
err = record.GetPBFieldValues(newMsg)
if err != nil {
logger.ERR("GetPBFieldValues failed %s", err.Error())
return
}
fmt.Println(tools.ConvertToJson(newMsg))
}
}
logger.INFO("batch field get success")
fmt.Println("batch field get success")
}
5. PBClient中的方法说明
/**
@brief 批量查询Generic表多条记录的部分value字段
@param [IN/OUT] msgs 由proto文件生成的记录结构体,入参需要填写完整主键字段;若查询成功,会更新为返回的记录
@param [IN/OUT] opt 可选参数。FieldNames必填;若有记录返回,会更新BatchResult和BatchVersion
@param [IN] zoneId 可选参数,不设置则取默认zone,默认zone可通过client.SetDefaultZoneId设置
@retval error 错误码
*/
func (c *PBClient) DoBatchFieldGet(msgs []proto.Message, opt *option.PBOpt, zoneId ...uint32) error
6. PBOpt中的相关参数说明
type PBOpt struct {
// 批量操作中每条记录的版本号,调用DoBatchFieldGet后填充
BatchVersion []int32
// 批量操作中每条记录的操作结果,调用DoBatchFieldGet后填充
BatchResult []error
// 如果此请求会返回多条记录,设置是否允许一个请求包自动响应多个应答包
// 1表示允许分包,0表示不允许分包
MultiFlag byte
// 超时时间,不设置默认使用客户端默认超时时间
Timeout time.Duration
// Go协程context,不设置默认使用Timeout,否则使用context判断协程退出
Ctx context.Context
// PB FieldGet和FieldSet使用,获取或更新部分字段;DoBatchFieldGet必填
FieldNames []string
}
7. 响应对象(response)中的方法说明
注:此处未列出的响应对象的其它方法,即表示该方法在批量部分字段查询的场景不需要使用,误用可能会导致报错。
/*
@brief 获取响应消息的结果
@retval 0 成功,其它值失败
*/
GetResult() int
/*
@brief 获取本响应中结果记录条数
@retval int 响应中结果记录条数
*/
GetRecordCount() int
/*
@brief 从结果中获取一条记录
@retval *record.Record 记录指针
@retval error 错误码
*/
FetchRecord() (*record.Record, error)
/*
@判断是否有更多的回包
@retval 1 有,0 没有
*/
HaveMoreResPkgs() int
8. 记录对象(record)中的方法说明
注:此处未列出的记录对象的其它方法,即表示该方法在批量部分字段查询的场景不需要使用,误用可能会导致报错。
/**
@brief 获取记录版本号
@retval 记录版本号
**/
func (r *Record) GetVersion() int32
/**
@brief 设置部分value字段,专用于field操作,TcaplusApiPBFieldGetReq、TcaplusApiPBFieldUpdateReq、TcaplusApiPBFieldIncreaseReq、TcaplusApiPBBatchFieldGetReq
@param [IN] message proto.Message 由proto文件生成的记录结构体
@param [IN] values []string 指定本次设置的value字段
@retval []byte 由记录key字段编码生成,由于多条记录的响应记录是无序的,可以用这个值来匹配记录
@retval error 错误码
**/
func (r *Record) SetPBFieldValues(message proto.Message, values []string) ([]byte, error)
/**
@brief 基于PB Message读取field操作返回的部分字段
@param [OUT] message proto.Message 由proto文件生成的记录结构体
@retval error 错误码
**/
func (r *Record) GetPBFieldValues(message proto.Message) error